# Copyright (c) 2018 Presto Labs Pte. Ltd.
# Author: leon

import datetime
import functools
import typing

from coin.exchange.base.kr_rest.futures_product import (NativeFuturesProduct,
                                                        NormFuturesProduct,
                                                        parse_futures_product_symbol)
from coin.exchange.huobi.config import futures_group_symbol
from coin.exchange.huobi.kr_rest.constants import currency_list
import coin.exchange.huobi_futures.kr_rest.contract_util as contract_util
from coin.exchange.huobi_futures.kr_rest.currency import HuobiCurrency


class HuobiFuturesProduct(NativeFuturesProduct):
  _exchange = 'Huobi'
  NativeCurrencyType = HuobiCurrency

  def __init__(self,
               base: HuobiCurrency,
               quote: HuobiCurrency,
               expiry: typing.Union[datetime.datetime, datetime.date, str],
               contract_type: str):
    assert quote.currency in ['USD', 'USDT'], quote.currency

    if contract_type.lower() == 'perpetual':
      native_symbol = '%s-%s' % (base.native_currency_futures, quote.native_currency_futures)
    else:
      assert quote.currency == 'USD', quote
      native_expiry_str = contract_util.to_native_expiry_str(expiry, contract_type)
      native_symbol = '%s%s' % (base.native_currency_futures, native_expiry_str)

    super().__init__(base=base, quote=quote, expiry=expiry, native_symbol=native_symbol)
    self._subscription_symbol = '%s-%s.%s' % (
        base.currency,
        quote.currency,
        contract_util.to_native_subscription_contract(contract_type)[0].upper())
    self._native_subscription_symbol = '%s_%s' % (
        base.native_currency_futures,
        contract_util.to_native_subscription_contract(contract_type)[1])
    self._contract_type = contract_type

  def __repr__(self):
    return str(super().__repr__(self.contract_type))

  @property
  def contract_type(self):
    return self._contract_type

  @classmethod
  @functools.lru_cache(maxsize=256)
  def FromProductImpl(cls, product, current_datetime):
    assert isinstance(product, NormFuturesProduct), type(product)
    assert product.quote.currency in ('USD', 'USDT'), product.quote.currency
    contract_type = contract_util.find_contract_type_from_expiry(current_datetime, product.expiry)
    return cls(product.base, product.quote, product.expiry, contract_type)

  @classmethod
  @functools.lru_cache(maxsize=256)
  def FromStrProductImpl(cls, product, current_datetime):
    assert isinstance(product, str), product

    parsed_result = parse_futures_product_symbol(product)
    assert parsed_result['quote'] in ('USD', 'USDT'), parsed_result['quote']
    expiry_str = parsed_result['expiry'].lower()
    expiry, contract_type = contract_util.to_expiry_contract_type(expiry_str, current_datetime)

    base = cls.NativeCurrencyType.FromStrCurrency(parsed_result['base'], current_datetime)
    quote = cls.NativeCurrencyType.FromStrCurrency(parsed_result['quote'], current_datetime)
    return cls(base, quote, expiry, contract_type)

  @classmethod
  @functools.lru_cache(maxsize=256)
  def FromStrNativeProductImpl(cls, product, current_datetime):
    assert isinstance(product, str), product

    # Native symbol string.
    if product.endswith('-USD') or product.endswith('-USDT'):
      base, quote = product.split('-')
      expiry_str = 'PERPETUAL'
    else:
      base = product[:-6]
      quote = 'USD'
      expiry_str = product[-6:]

    expiry, contract_type = contract_util.parse_native_expiry_contract_type(
        expiry_str, current_datetime)
    base = cls.NativeCurrencyType.FromStrNativeCurrency(base, current_datetime)
    quote = cls.NativeCurrencyType.FromStrNativeCurrency(quote, current_datetime)
    return cls(base, quote, expiry, contract_type)

  @classmethod
  @functools.lru_cache(maxsize=256)
  def FromCurrencyPair2AndContractTypeImpl(cls, base, quote, contract_type, current_datetime):
    expiry = contract_util.find_expiry_from_contract_type(current_datetime, contract_type)
    assert quote.currency == 'USD', quote.currency
    return cls(base, quote, expiry, contract_type)

  @classmethod
  @functools.lru_cache(maxsize=256)
  def FromCurrencyPairAndContractTypeImpl(cls, currency_pair, contract_type, current_datetime):
    expiry = contract_util.find_expiry_from_contract_type(current_datetime, contract_type)
    pair = currency_pair.split('_')
    assert len(pair) == 2, pair
    assert pair[1] == 'USD', pair
    base = HuobiCurrency.FromStrNativeCurrency(pair[0], current_datetime)
    quote = HuobiCurrency.FromStrCurrency('USD', current_datetime)
    return cls(base, quote, expiry, contract_type)


def get_huobi_futures_product_list(current_datetime=None):
  product_list = []
  for group in futures_group_symbol.values():
    for native_subscription_symbol in group:
      exploded = native_subscription_symbol.split('_', 1)
      base = HuobiCurrency.FromStrNativeCurrency(exploded[0], current_datetime)
      quote = HuobiCurrency.FromStrCurrency('USD', current_datetime)
      contract_type = exploded[1]
      try:
        product = HuobiFuturesProduct.FromCurrencyPair2AndContractType(
            base, quote, contract_type, current_datetime)
        product_list.append(product)
      except Exception as e:
        print('Fail to generate Huobi futures product.',
              '%s%s.%s' % (base, quote, contract_type),
              type(e),
              e)
        continue
  return product_list


def get_huobi_futures_product_list_grouped(current_datetime=None):
  product_list = []
  for group in futures_group_symbol.values():
    product_list_group = []
    for native_subscription_symbol in group:
      exploded = native_subscription_symbol.split('_', 1)
      base = HuobiCurrency.FromStrNativeCurrency(exploded[0], current_datetime)
      quote = HuobiCurrency.FromStrCurrency('USD', current_datetime)
      contract_type = contract_util.to_contract_type(exploded[1])
      try:
        product = HuobiFuturesProduct.FromCurrencyPair2AndContractType(
            base, quote, contract_type, current_datetime)
        product_list_group.append(product)
      except Exception as e:
        print('Fail to generate Huobi futures product.',
              '%s%s.%s' % (base, quote, contract_type),
              type(e),
              e)
        continue
    product_list.append(product_list_group)
  return product_list
